#include "ThreadPool.h"
#include <iostream>

ThreadPool* ThreadPool::_instance = NULL;

ThreadPool *ThreadPool::GetThreadPool()
{
	return ThreadPool::_instance;
}

void ThreadPool::InitThreadPool(int nbThread)
{
	ThreadPool::_instance = new ThreadPool(nbThread);
}

ThreadPool::ThreadPool(int nbThread) :
_maxThread(nbThread),
_curThread(0),
_freeThread(0)
{
	InitializeCriticalSection(&_waitingRequest);
	InitializeCriticalSection(&_accessingRequest);
	InitializeCriticalSection(&_infoThread);
	_requestAvailable    = CreateEvent(NULL, true, false, NULL);
}


void   ThreadPool::checkThread()
{
	int currRequest =	this->_moderatePriorityRequest.size() +
		this->_highPriorityRequest.size() + this->_lowPriorityRequest.size();
	if (this->_freeThread >= currRequest || this->_curThread >= this->_maxThread)
		return;
	EnterCriticalSection(&this->_infoThread);
	this->_freeThread++;
	this->_curThread++;
	LeaveCriticalSection(&this->_infoThread);
	
	HANDLE h = CreateThread(NULL, 
							0,
							ThreadProc,
							this,
							0,
							NULL
							);
	std::cout << "nbthread : " << _curThread << std::endl;
}

DWORD WINAPI ThreadProc (LPVOID lpParameter)
{
	ThreadPool *param = (ThreadPool*) lpParameter;
	ThreadPool::Request *request = NULL;

	while (true)
	{
		if (request)
		{
			(*request->function)(request->parameters);
			if (request->autoFree)
				delete request;
			EnterCriticalSection(&param->_infoThread);
			param->_freeThread++;
			std::cout << "nbFREE thread : " << param->_freeThread << std::endl;
			LeaveCriticalSection(&param->_infoThread);
		}
	EnterCriticalSection(&param->_waitingRequest);
	WaitForSingleObject(param->_requestAvailable, INFINITE);

	
	EnterCriticalSection(&param->_infoThread);
	param->_freeThread--;
	LeaveCriticalSection(&param->_infoThread);


	EnterCriticalSection(&param->_accessingRequest);
	if (!param->_highPriorityRequest.empty())
	{
		request = param->_highPriorityRequest.front();
		param->_highPriorityRequest.pop_front();
	}
	else if (!param->_moderatePriorityRequest.empty())
	{
		request = param->_moderatePriorityRequest.front();
		param->_moderatePriorityRequest.pop_front();
	}
	else if (!param->_lowPriorityRequest.empty())
	{
		request = param->_lowPriorityRequest.front();
		param->_lowPriorityRequest.pop_front();
	}
	if (param->_moderatePriorityRequest.empty() &&
		param->_lowPriorityRequest.empty() &&
		param->_highPriorityRequest.empty())
		ResetEvent(param->_requestAvailable);
	LeaveCriticalSection(&param->_accessingRequest);
	LeaveCriticalSection(&param->_waitingRequest);
	}
	return 0;
}


void ThreadPool::AddRequest(ThreadPool::Request *request, ThreadPool::Priority priority)
{
	EnterCriticalSection(&_accessingRequest);
	if (priority == HIGH)
		this->_highPriorityRequest.push_back(request);
	else if (priority == MODERATE)
		this->_moderatePriorityRequest.push_back(request);
	else
		this->_lowPriorityRequest.push_back(request);
	this->checkThread();
	SetEvent(this->_requestAvailable);
	LeaveCriticalSection(&_accessingRequest);
}